<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><title>RocksDB（三）：WAL Learning | WLeoo's Blog</title><meta name="keywords" content="RocksDB,数据库"><meta name="author" content="WLeoo,3049458366@qq.com"><meta name="copyright" content="WLeoo"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="WAL LearningWrite Ahead LogOverview在RocksDB中每一次数据的更新都会涉及到两个结构，一个是内存中的memtable(后续会刷新到磁盘成为SST)，第二个是WAL(WriteAheadLog)。在默认情况下爱，RocksDB通过在每次用户写时调用flush （刷盘）WAL文件来保持一致性。 WAL主要的功能是当RocksDB异常退出后，能够恢复出错前的内存中(">
<meta property="og:type" content="article">
<meta property="og:title" content="RocksDB（三）：WAL Learning">
<meta property="og:url" content="https://gitee.com/WLeoo/wleoo.git/2022/12/07/RocksDB%EF%BC%88%E4%B8%89%EF%BC%89%EF%BC%9AWAL-Learning/index.html">
<meta property="og:site_name" content="WLeoo&#39;s Blog">
<meta property="og:description" content="WAL LearningWrite Ahead LogOverview在RocksDB中每一次数据的更新都会涉及到两个结构，一个是内存中的memtable(后续会刷新到磁盘成为SST)，第二个是WAL(WriteAheadLog)。在默认情况下爱，RocksDB通过在每次用户写时调用flush （刷盘）WAL文件来保持一致性。 WAL主要的功能是当RocksDB异常退出后，能够恢复出错前的内存中(">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070729354.jpg">
<meta property="article:published_time" content="2022-12-06T20:38:42.000Z">
<meta property="article:modified_time" content="2022-12-06T23:29:31.437Z">
<meta property="article:author" content="WLeoo">
<meta property="article:tag" content="RocksDB">
<meta property="article:tag" content="数据库">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070729354.jpg"><link rel="shortcut icon" href="/WLeoo/"><link rel="canonical" href="https://gitee.com/WLeoo/wleoo.git/2022/12/07/RocksDB%EF%BC%88%E4%B8%89%EF%BC%89%EF%BC%9AWAL-Learning/"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/WLeoo/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/WLeoo/',
  algolia: undefined,
  localSearch: {"path":"search.xml","languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: {"defaultEncoding":2,"translateDelay":0,"msgToTraditionalChinese":"繁","msgToSimplifiedChinese":"簡"},
  noticeOutdate: {"limitDay":365,"position":"top","messagePrev":"It has been","messageNext":"days since the last update, the content of the article may be outdated."},
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":200},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: {"limitCount":50,"languages":{"author":"作者: WLeoo","link":"链接: ","source":"来源: WLeoo's Blog","info":"著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。"}},
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'RocksDB（三）：WAL Learning',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2022-12-07 07:29:31'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js"></script><script src="https://cdn.jsdelivr.net/gh/metowolf/MetingJS@1.2/dist/Meting.min.js"></script><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><link rel="stylesheet" href="/WLeoo/css/wleoo.css"><!-- hexo injector head_end start --><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper/swiper-bundle.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Zfour/Butterfly-card-history/baiduhistory/css/main.css"><!-- hexo injector head_end end --><meta name="generator" content="Hexo 5.4.0"></head><body><div id="loading-box"><div class="loading-left-bg"></div><div class="loading-right-bg"></div><div class="spinner-box"><div class="configure-border-1"><div class="configure-core"></div></div><div class="configure-border-2"><div class="configure-core"></div></div><div class="loading-word">加载中...</div></div></div><div id="web_bg"></div><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="/WLeoo/img/avatar.png" onerror="onerror=null;src='/img/ali.gif'" alt="avatar"/></div><div class="site-data is-center"><div class="data-item"><a href="/WLeoo/archives/"><div class="headline">文章</div><div class="length-num">13</div></a></div><div class="data-item"><a href="/WLeoo/tags/"><div class="headline">标签</div><div class="length-num">17</div></a></div><div class="data-item"><a href="/WLeoo/categories/"><div class="headline">分类</div><div class="length-num">16</div></a></div></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/WLeoo/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/WLeoo/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/WLeoo/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/WLeoo/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fa fa-heartbeat"></i><span> 清单</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/WLeoo/music/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/WLeoo/Gallery/"><i class="fa-fw fas fa-images"></i><span> 照片</span></a></li><li><a class="site-page child" href="/WLeoo/movies/"><i class="fa-fw fas fa-video"></i><span> 电影</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/WLeoo/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/WLeoo/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070729354.jpg')"><nav id="nav"><span id="blog_name"><a id="site-name" href="/WLeoo/">WLeoo's Blog</a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/WLeoo/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/WLeoo/archives/"><i class="fa-fw fas fa-archive"></i><span> 时间轴</span></a></div><div class="menus_item"><a class="site-page" href="/WLeoo/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/WLeoo/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page group" href="javascript:void(0);"><i class="fa-fw fa fa-heartbeat"></i><span> 清单</span><i class="fas fa-chevron-down"></i></a><ul class="menus_item_child"><li><a class="site-page child" href="/WLeoo/music/"><i class="fa-fw fas fa-music"></i><span> 音乐</span></a></li><li><a class="site-page child" href="/WLeoo/Gallery/"><i class="fa-fw fas fa-images"></i><span> 照片</span></a></li><li><a class="site-page child" href="/WLeoo/movies/"><i class="fa-fw fas fa-video"></i><span> 电影</span></a></li></ul></div><div class="menus_item"><a class="site-page" href="/WLeoo/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/WLeoo/about/"><i class="fa-fw fas fa-heart"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">RocksDB（三）：WAL Learning</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2022-12-06T20:38:42.000Z" title="发表于 2022-12-07 04:38:42">2022-12-07</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2022-12-06T23:29:31.437Z" title="更新于 2022-12-07 07:29:31">2022-12-07</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/WLeoo/categories/RocksDB/">RocksDB</a><i class="fas fa-angle-right post-meta-separator"></i><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/WLeoo/categories/RocksDB/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="RocksDB（三）：WAL Learning"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h1 id="WAL-Learning"><a href="#WAL-Learning" class="headerlink" title="WAL Learning"></a>WAL Learning</h1><h2 id="Write-Ahead-Log"><a href="#Write-Ahead-Log" class="headerlink" title="Write Ahead Log"></a>Write Ahead Log</h2><h3 id="Overview"><a href="#Overview" class="headerlink" title="Overview"></a>Overview</h3><p>在RocksDB中每一次数据的更新都会涉及到两个结构，一个是内存中的memtable(后续会刷新到磁盘成为SST)，第二个是WAL(WriteAheadLog)。在默认情况下爱，RocksDB通过在每次用户写时调用flush （刷盘）WAL文件来保持一致性。</p>
<p>WAL主要的功能是当RocksDB异常退出后，能够恢复出错前的内存中(memtable)数据，因此RocksDB默认是每次用户写都会刷新数据到WAL。每次当当前WAL对应的内存数据(memtable)刷新到磁盘之后，都会新建一个WAL。</p>
<p>所有的WAL文件都是保存在WAL目录(options.wal_dir)，为了保证数据的状态，所有的WAL文件的名字都是按照顺序的(log_number)。</p>
<p>一个WAL记录着所有列族的写入日志。</p>
<h3 id="Life-Cycle-of-a-WAL"><a href="#Life-Cycle-of-a-WAL" class="headerlink" title="Life Cycle of a WAL"></a>Life Cycle of a WAL</h3><p>举个例子，Rocksdb实例创建了两个column families（列族），分别是<code>new_cf</code>和<code>default</code>。一旦有db被打开，就会在磁盘上创建一个WAL来持久化所有的写操作。（WAL在所有列族之间是共享的。）</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">DB* db;</span><br><span class="line">std::vector&lt;ColumnFamilyDescriptor&gt; column_families;</span><br><span class="line">column_families.push_back(ColumnFamilyDescriptor(</span><br><span class="line">    kDefaultColumnFamilyName, ColumnFamilyOptions()));</span><br><span class="line">column_families.push_back(ColumnFamilyDescriptor(</span><br><span class="line">    &quot;new_cf&quot;, ColumnFamilyOptions()));</span><br><span class="line">std::vector&lt;ColumnFamilyHandle*&gt; handles;</span><br><span class="line">s &#x3D; DB::Open(DBOptions(), kDBPath, column_families, &amp;handles, &amp;db);</span><br></pre></td></tr></table></figure>

<p>在两个列族中添加一下kv对数据：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">db-&gt;Put(WriteOptions(), handles[1], Slice(&quot;key1&quot;), Slice(&quot;value1&quot;));</span><br><span class="line">db-&gt;Put(WriteOptions(), handles[0], Slice(&quot;key2&quot;), Slice(&quot;value2&quot;));</span><br><span class="line">db-&gt;Put(WriteOptions(), handles[1], Slice(&quot;key3&quot;), Slice(&quot;value3&quot;));</span><br><span class="line">db-&gt;Put(WriteOptions(), handles[0], Slice(&quot;key4&quot;), Slice(&quot;value4&quot;));</span><br></pre></td></tr></table></figure>

<p>此时WAL已经记录了所有的写入操作。接着WAL会保持着打开的状态，并且持续记录将来的写入操作一直到WAL的大小达<code>DBOptions::max_total_wal_size</code><br>为止。<br>如果使用者决定将列族刷新到磁盘上，会产生以下改变：<br>1.<code>new_cf</code>的数据（key1和key3）会被刷新成为一个新的SST file。</p>
<ol start="2">
<li>一个新的WAL会被创建，并且后续对于所有的列族的写入操作都会被记录在新的WAL上</li>
<li>旧的WAL将不会再记录新的写入操作但是对于旧WAL的删除会被推迟。</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">db-&gt;Flush(FlushOptions(), handles[1]);</span><br><span class="line">&#x2F;&#x2F; key5 and key6 will appear in a new WAL</span><br><span class="line">db-&gt;Put(WriteOptions(), handles[1], Slice(&quot;key5&quot;), Slice(&quot;value5&quot;));</span><br><span class="line">db-&gt;Put(WriteOptions(), handles[0], Slice(&quot;key6&quot;), Slice(&quot;value6&quot;));</span><br></pre></td></tr></table></figure>

<p>此时会有两个WAL，旧的WAL包括对于key1到key4的记录，而新的WAL包括key5与key6的记录。因为旧的WAL仍然包括至少一个default列族的有效数据，所以现在它还不能被删除。只有当default列族被flush后（无论是自动的刷盘操作还是手动的刷盘操作），这个旧的WAL可以被归档并标记为删除。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">db-&gt;Flush(FlushOptions(), handles[0]);</span><br><span class="line">&#x2F;&#x2F; The older WAL will be archived and purged separately</span><br></pre></td></tr></table></figure>

<p>所以总结以下，一个WAL只有在下面两种情况下才会被创建：<br>1.当一个新的数据库被打开<br>2.当一个列族被flush</p>
<p>只有当所有的列族都被flush到SST中并超出WAL所能容纳的最大序号时，或者说，当所有WAL中的数据都已经被持久化到SST文件中时，一个WAL文件才会被删除（或者说被归档，如果启用了存档的话）。而归档的文件会被移动到一个单独的位置，后续从磁盘中删除。处于复制的目的，所以实际上的删除可能还会被延迟，详见下面关于事务日志迭代器的部分。</p>
<h3 id="WAL-Configurations"><a href="#WAL-Configurations" class="headerlink" title="WAL Configurations"></a>WAL Configurations</h3><h4 id="DBOptions-wal-dir"><a href="#DBOptions-wal-dir" class="headerlink" title="DBOptions::wal_dir"></a>DBOptions::wal_dir</h4><p><code>DBOptions::wal_dir</code>可以设置RocksDB保存WAL文件的目录，这使得WAL的文件可以保存在与实际数据不同的目录下。</p>
<h4 id="DBOptions-WAL-ttl-seconds-DBOptions-WAL-size-limit-MB"><a href="#DBOptions-WAL-ttl-seconds-DBOptions-WAL-size-limit-MB" class="headerlink" title="DBOptions::WAL_ttl_seconds,DBOptions::WAL_size_limit_MB"></a>DBOptions::WAL_ttl_seconds,DBOptions::WAL_size_limit_MB</h4><p>这两个选项会影响归档的WAL被删除的快慢。非零值表示触发归档的 WAL 删除的时间和磁盘空间阈值。</p>
<h4 id="DBOptions-max-total-wal-size"><a href="#DBOptions-max-total-wal-size" class="headerlink" title="DBOptions::max_total_wal_size"></a>DBOptions::max_total_wal_size</h4><p>为了限制WAL文件数目的大小，RocksDB使用<code>DBOptions::max_total_wal_size</code>来出发列族的flush操作。一旦WAL文数目件超出限定大小，RocksDB会开始强制将所有列族的数据都flush，以便于删除一些旧的WAL文件。当列族以非均匀的速率更新时，这个配置会很有用。如果没有大小限制的话，当不经常更新的列族一段时间没有flush时，用户可能会一直保留着旧的WAL文件。</p>
<h4 id="DBOptions-avoid-flush-during-recovery"><a href="#DBOptions-avoid-flush-during-recovery" class="headerlink" title="DBOptions::avoid_flush_during_recovery"></a>DBOptions::avoid_flush_during_recovery</h4><p>顾名思义，这个配置就是为了避免在recovery的时候进行flush。</p>
<h4 id="DBOptions-manual-wal-flush"><a href="#DBOptions-manual-wal-flush" class="headerlink" title="DBOptions::manual_wal_flush"></a>DBOptions::manual_wal_flush</h4><p><code>DBOptions::manual_wal_flush</code>决定了是否每次在写操作后对WAL自动进行flush或者纯手动执行。（用户通过地爱用<code>WALflush</code>来触发WAL的flush）</p>
<h4 id="DBOptions-wal-filter"><a href="#DBOptions-wal-filter" class="headerlink" title="DBOptions::wal_filter"></a>DBOptions::wal_filter</h4><p>通过<code>DBOptions::wal_filter</code>，用户可以提供在恢复数据期间处理WAL时所要调用的过滤器对象。(恢复数据时可以过滤某些记录)</p>
<h4 id="DBOptions-wal-compression"><a href="#DBOptions-wal-compression" class="headerlink" title="DBOptions::wal_compression"></a>DBOptions::wal_compression</h4><p>压缩算法用于压缩WAL记录。默认是<code>kNoCompression</code>.</p>
<h4 id="WriteOptions-disableWAL"><a href="#WriteOptions-disableWAL" class="headerlink" title="WriteOptions::disableWAL"></a>WriteOptions::disableWAL</h4><p>用来打开或者关闭WAL支持。当用户依赖其他方法来记录或者不在意数据的丢失时，可以调用<code>WriteOptions::disableWAL</code>。</p>
<h3 id="Transaction-Log-Iterator"><a href="#Transaction-Log-Iterator" class="headerlink" title="Transaction Log Iterator"></a>Transaction Log Iterator</h3><p>事务日志迭代器提供了一种在 RocksDB 实例之间进行复制数据的方法。一旦 WAL 由于列族刷新而被归档，WAL 并不会立即删除。目的是允许事务日志迭代器继续读取 WAL 并将其发送给从节点进行重现。</p>
<h2 id="WAL-Recovery-Modes"><a href="#WAL-Recovery-Modes" class="headerlink" title="WAL Recovery Modes"></a>WAL Recovery Modes</h2><h3 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h3><p>每一个应用都是唯一的，都需要RocksDB<br>保证确定状态的一致性。RocksDB中每一次提交记录都是持久化的。没有被提交的记录被记载在WAL中。当RocksDB正常推出后，所有未提交的数据都会在关闭之前提交，因此始终可以保持一致性。当RocksDB进程被kill或者服务器重启后，RocksDB需要重新恢复到一致性状态。</p>
<h2 id="kTolerateCorruptedTailRecords"><a href="#kTolerateCorruptedTailRecords" class="headerlink" title="kTolerateCorruptedTailRecords"></a>kTolerateCorruptedTailRecords</h2><p>在这种模式下，WAL的重现会忽略日志末尾的任何错误。这些错误主要是，在不安全的退出下，在日志的末尾可能会有一些没有完成的写操作。这是一种启发式模式，系统无法区分日志尾部的损坏和不完整的写入。任何其他 IO 错误都将被视为数据损坏。<br>这种模式为大部分应用所使用，这是因为该模式提在在不安全退出与数据一致性之间做出了适当权衡。</p>
<h2 id="kAbsoluteConsistency"><a href="#kAbsoluteConsistency" class="headerlink" title="kAbsoluteConsistency"></a>kAbsoluteConsistency</h2><p>在这种模式下，WAL重现期间的任何IO错误都会被视作数据损坏。这种模式适用于以下场景：应用无法接受失去任何一条记录的风险或者应用有其他恢复未提交数据的方法。</p>
<h2 id="kPointInTimeRecovery"><a href="#kPointInTimeRecovery" class="headerlink" title="kPointInTimeRecovery"></a>kPointInTimeRecovery</h2><p>在这种模式下，当遇到一个IO错误时，WAL的重现工作会停止。系统会恢复到一个满足一致性的时间点上。该模式适用于那些拥有副本的集群。来自另一个副本的数据可以用来恢复剩下的数据。</p>
<h2 id="kSkipAnyCorruptedRecords"><a href="#kSkipAnyCorruptedRecords" class="headerlink" title="kSkipAnyCorruptedRecords"></a>kSkipAnyCorruptedRecords</h2><p>在这种模式下，读取日志时会忽略任何IO错误。系统尽最大努力去恢复尽可能多的数据。该模式适用于灾难性毁坏的修复。</p>
<h2 id="WAL-File-Format"><a href="#WAL-File-Format" class="headerlink" title="WAL File Format"></a>WAL File Format</h2><h3 id="Overview-1"><a href="#Overview-1" class="headerlink" title="Overview"></a>Overview</h3><p>WAL将memtable的操作序列化从而持久化存储为日志文件。当db故障时，可以使用WAL文件来根据记录重建memtable，从而恢复数据库回到一致性状态。当一个memtable被安全地flush到持久化存储的文件中，相应的WAL日志会被淘汰从而归档。最终经过一段时间后，被归档的记录都会从磁盘中被清除。</p>
<h3 id="WAL-Manager"><a href="#WAL-Manager" class="headerlink" title="WAL Manager"></a>WAL Manager</h3><p>在WAL的目录中，WAL文件按照序列号递增的顺序命名。为了重建数据库之前的一致性状态，这些文件会按照序列号递增的顺序读取。WAL Manager 封装了读取WAL文件的操作。在封装内部，采用Reader or Writer abstraction来读取文件。</p>
<h2 id="Reader-Writer"><a href="#Reader-Writer" class="headerlink" title="Reader/Writer"></a>Reader/Writer</h2><p>Writer将记录更新到日志文件中的相关操作进行封装。这中间具体的内部细节由WriteableFile接口来处理实现。同样的，Reader将从日志文件中顺序读取记录的操作进行封装。这中间具体的内部细节由SequentialFile接口来处理实现。</p>
<h2 id="Log-File-Format"><a href="#Log-File-Format" class="headerlink" title="Log File Format"></a>Log File Format</h2><p>WAL文件时由一堆变长的record组成的，而每个record是由kBlockSize（32K）来分组（就是一个kBlockSize由多个record组成），如果某一个record大于kBlockSize的话，它就会被切分为多个record（通过type来判断）。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">      +-----+-------------+--+----+----------+------+-- ... ----+</span><br><span class="line">File  | r0  |        r1   |P | r2 |    r3    |  r4  |           |</span><br><span class="line">      +-----+-------------+--+----+----------+------+-- ... ----+</span><br><span class="line">      &lt;--- kBlockSize ------&gt;|&lt;-- kBlockSize ------&gt;|</span><br><span class="line"></span><br><span class="line"> rn &#x3D; variable size records</span><br><span class="line"> P &#x3D; Padding</span><br></pre></td></tr></table></figure>

<ul>
<li>rn：代表第n个record</li>
<li>p：代表当无法组成一个kBlockSize时的填充，即如果一个特定的记录不能完全适配剩余的空间，那么就会把剩余的空间补零。</li>
</ul>
<h3 id="Record-Format"><a href="#Record-Format" class="headerlink" title="Record Format"></a>Record Format</h3><p>记录的格式展示如下。总共由两种记录格式，分为<code>Legacy</code>和<code>Recyclable</code>。</p>
<h4 id="The-Legacy-Record-Format"><a href="#The-Legacy-Record-Format" class="headerlink" title="The Legacy Record Format"></a>The Legacy Record Format</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">+---------+-----------+-----------+--- ... ---+</span><br><span class="line">|CRC (4B) | Size (2B) | Type (1B) | Payload   |</span><br><span class="line">+---------+-----------+-----------+--- ... ---+</span><br><span class="line"></span><br><span class="line">CRC &#x3D; 32bit hash computed over the payload using CRC</span><br><span class="line">Size &#x3D; Length of the payload data</span><br><span class="line">Type &#x3D; Type of record</span><br><span class="line">       (kZeroType, kFullType, kFirstType, kLastType, kMiddleType )</span><br><span class="line">       The type is used to group a bunch of records together to represent</span><br><span class="line">       blocks that are larger than kBlockSize</span><br><span class="line">Payload &#x3D; Byte stream as long as specified by the payload size</span><br></pre></td></tr></table></figure>

<ul>
<li>CRC：循环冗余校验码，32bit，用CRC计算在有效负载上的hash</li>
<li>Size：是有效负载的数据量的长度</li>
<li>Payload：由有效负载大小规定的字节流大小，其实就是承载的数据</li>
<li>Type：记录的种类，种类是用来对一大堆记录进行分组从而更好的展现出来，包括kZeroType, kFullType, kFirstType, kLastType, kMiddleType这些种类。后续详细说下这些种类。</li>
</ul>
<h4 id="The-Recyclable-Record-Format"><a href="#The-Recyclable-Record-Format" class="headerlink" title="The Recyclable Record Format"></a>The Recyclable Record Format</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">+---------+-----------+-----------+----------------+--- ... ---+</span><br><span class="line">|CRC (4B) | Size (2B) | Type (1B) | Log number (4B)| Payload   |</span><br><span class="line">+---------+-----------+-----------+----------------+--- ... ---+</span><br><span class="line">Same as above, with the addition of</span><br><span class="line">Log number &#x3D; 32bit log file number, so that we can distinguish between</span><br><span class="line">records written by the most recent log writer vs a previous one.</span><br></pre></td></tr></table></figure>

<p>Log number（32bit）是文件编号，用来区别最新的log writter写的记录和之前的记录。</p>
<h4 id="Record-Format-Details-For-Legacy-Format"><a href="#Record-Format-Details-For-Legacy-Format" class="headerlink" title="Record Format Details For Legacy Format"></a>Record Format Details For Legacy Format</h4><p>日志文件是由连续地32KB大小的block组成。唯一的例外是文件尾部数据包含一个不是完整的block。<br>每一个block包含连续的Records，record的格式如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">block :&#x3D; record* trailer?</span><br><span class="line">record :&#x3D;</span><br><span class="line">  checksum: uint32	&#x2F;&#x2F; crc32c of type and data[]</span><br><span class="line">  length: uint16</span><br><span class="line">  type: uint8		&#x2F;&#x2F; One of FULL, FIRST, MIDDLE, LAST </span><br><span class="line">  data: uint8[length]</span><br></pre></td></tr></table></figure>

<p>记录永远不会在块的最后六个字节内开始（因为它不适合）。此处的任何剩余字节构成trailer，trailer必须完全由零字节组成，并且必须被readers跳过。</p>
<blockquote>
<p>如果当前块中只剩下七个字节，并且添加了一条新的非零长度记录，则writer必须发出一条 FIRST 记录（其中包含零字节的用户数据）以填充块中剩下的七个字节，然后在后续块中存放所有用户数据。</p>
</blockquote>
<p>后续还会添加更多的types。因为无法识别记录的types，一些Readers可能会跳过阅读types，其他的Readers可能会报告一些数据被跳过。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">FULL &#x3D;&#x3D; 1</span><br><span class="line">FIRST &#x3D;&#x3D; 2</span><br><span class="line">FIRST &#x3D;&#x3D; 3</span><br><span class="line">LAST &#x3D;&#x3D; 4</span><br></pre></td></tr></table></figure>

<p>其中FULL表示包含了全部用户record记录，FIRST、MIDDLE、LAST用户表示一个record因为太大然后拆分为多个数据片。FIRST表示是用户record的第一块数据，LAST表示最后一块数据，MID表示record中间部分的数据。举一个简单的例子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">A：length 1000</span><br><span class="line">B：length 97270</span><br><span class="line">C：length 8000</span><br></pre></td></tr></table></figure>

<p>A会在第一个block中存储为一个完整的record。<br>B会因为超出了一个block的大小（32KB=32768B），所以B会被拆分为3个分片，第一个分片占用了第一个block的剩余全部空间，第二个分片占用第二个block的全部空间，第三个分片占用第三个block的前面部分空间。会在第三个block中预留6个字节作为trailer，置为空来表示结束。<br>C将会完整地存储在第四个block。</p>
<h4 id="payload"><a href="#payload" class="headerlink" title="payload"></a>payload</h4><p>最后是WAL的payload的格式，其实是一批操作的集合，从record中可以看出wal的写入是一批一批写入的。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; WriteBatch::rep_ :&#x3D;</span><br><span class="line">&#x2F;&#x2F;    sequence: fixed64</span><br><span class="line">&#x2F;&#x2F;    count: fixed32</span><br><span class="line">&#x2F;&#x2F;    data: record[count]</span><br><span class="line">&#x2F;&#x2F; record :&#x3D;</span><br><span class="line">&#x2F;&#x2F;    kTypeValue varstring varstring</span><br><span class="line">&#x2F;&#x2F;    kTypeDeletion varstring</span><br><span class="line">&#x2F;&#x2F;    kTypeSingleDeletion varstring</span><br><span class="line">&#x2F;&#x2F;    kTypeMerge varstring varstring</span><br><span class="line">&#x2F;&#x2F;    kTypeColumnFamilyValue varint32 varstring varstring</span><br><span class="line">&#x2F;&#x2F;    kTypeColumnFamilyDeletion varint32 varstring varstring</span><br><span class="line">&#x2F;&#x2F;    kTypeColumnFamilySingleDeletion varint32 varstring varstring</span><br><span class="line">&#x2F;&#x2F;    kTypeColumnFamilyMerge varint32 varstring varstring</span><br><span class="line">&#x2F;&#x2F;    kTypeBeginPrepareXID varstring</span><br><span class="line">&#x2F;&#x2F;    kTypeEndPrepareXID</span><br><span class="line">&#x2F;&#x2F;    kTypeCommitXID varstring</span><br><span class="line">&#x2F;&#x2F;    kTypeRollbackXID varstring</span><br><span class="line">&#x2F;&#x2F;    kTypeNoop</span><br><span class="line">&#x2F;&#x2F; varstring :&#x3D;</span><br><span class="line">&#x2F;&#x2F;    len: varint32</span><br><span class="line">&#x2F;&#x2F;    data: uint8[len]</span><br></pre></td></tr></table></figure>

<p>从上面的格式中可以看出有一个sequence的值，这个值主要用来表示WAL中操作的时序，这里要注意的每次sequence的更新是按照WriteBatch来更新的。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Status DBImpl::WriteToWAL(const WriteThread::WriteGroup&amp; write_group,</span><br><span class="line">                          log::Writer* log_writer, uint64_t* log_used,</span><br><span class="line">                          bool need_log_sync, bool need_log_dir_sync,</span><br><span class="line">                          SequenceNumber sequence) &#123;</span><br><span class="line">  Status status;</span><br><span class="line">.........................................</span><br><span class="line">  WriteBatchInternal::SetSequence(merged_batch, sequence);</span><br></pre></td></tr></table></figure>

<h3 id="Benefits"><a href="#Benefits" class="headerlink" title="Benefits"></a>Benefits</h3><p><code>recordio</code>的记录格式有以下好处：<br>1.我们不需要任何启发式方法进行重新同步 - 只需转到下一个block的边界并扫描即可。如果存在损坏，请跳到下一个block。作为附带好处，当一个日志文件的部分内容作为记录嵌入到另一个日志文件中时，我们并不会混淆。<br>2.在近似边界处拆分（例如，对于<code>mapreduce</code>）很简单：找到下一个block边界并跳过记录，直到我们遇到一个<code>FULL</code>或<code>FIRST</code>记录。<br>3.对于大型记录，我们不需要额外的缓冲。</p>
<h3 id="Downsides"><a href="#Downsides" class="headerlink" title="Downsides"></a>Downsides</h3><p><code>recordio</code>的记录格式有以下弊端：<br>1.没有封装更小的记录。但这可以通过添加新的记录类型来解决，因此这是当前实现的缺点。<br>2.无压缩。同样，可以通过添加新的记录类型来解决此问题。</p>
<h2 id="WAL-Performance"><a href="#WAL-Performance" class="headerlink" title="WAL Performance"></a>WAL Performance</h2><h3 id="Non-Sync-Mode"><a href="#Non-Sync-Mode" class="headerlink" title="Non-Sync Mode"></a>Non-Sync Mode</h3><p>当<code>WriteOptions.sync = false</code>时（默认就是false），WAL的写操作不会被同步到磁盘上。除非操作系统认为必须要进行flush（比如有太多的脏页的情况），用户不需要等待任何IO进行写入。<br>而希望可以减少因为写入操作系统页面缓存导致的 CPU 延迟的用户可以选择<code>Options.manual_wal_flush = true</code>。在这种选项下，WAL的写入甚至不会flush到文件系统的页面缓存中，但是会被保存在RocksDB中。用户可以通过调用<code>DB::FlushWAL()</code>来将缓存的条目转移到文件系统。<br>用户也可以通过调用<code>DB::SyncWAL()</code>来强制同步WAL文件。该函数不会阻止在其他线程中执行的写入。<br>在这种模式下，WAL的写在崩溃情况下时不安全的。</p>
<h3 id="Sync-Mode"><a href="#Sync-Mode" class="headerlink" title="Sync Mode"></a>Sync Mode</h3><p>当<code>WriteOptions.sync = true</code>时，前，WAL文件会在返回给用户之前被同步。</p>
<h4 id="Group-Commit"><a href="#Group-Commit" class="headerlink" title="Group Commit"></a>Group Commit</h4><p>正如其他一些依赖日志型操作的系统一样，RocksDB支持<code>group commit</code>来提高WAL的写入吞吐量以及写入放大。RocksDB的group commit是用简单的方式实现的：当不同的线程同时写入同一个数据库时，所有符合组合条件的未完成写入将被组合在一起并写入 WAL 一次，只需一次同步。通过这种方式，相同数量的IO可以完成更多的写入。<br>然而通过不同的写入操作进行的写入可能会不可以组合。组合的最大容量时1MB。RocksDB通过主动延迟写入来避免增加组合容量的大小。</p>
<h4 id="Number-of-I-Os-per-write"><a href="#Number-of-I-Os-per-write" class="headerlink" title="Number of I/Os per write"></a>Number of I/Os per write</h4><p>如果<code>Options.recycle_log_file_num = false</code>（默认就是false），RocksDB总会为新WAL的文段创建新文件。每一次的WAL写入都会改变数据和文件大小，因此每次同步将产生两个IO，一个用于修改数据，一个用于修改元数据。注意，RocksDB可以通过调用<code>fallocate()</code>来为文件预存空间，但同步时并不会阻塞元数据的IO。<br><code>Options.recycle_log_file_num = true</code>会保留一个WAL文件池并尝试重新使用它们。当写入现有存在的日志文件时，会使用随机写入（写入的大小最小为0）。在写入到达文件末尾之前，文件大小不会更改，因此可以避免元数据产生的 I/O（也取决于文件系统装载选项）。假设大多数的WAL文件拥有差不多的大小，那么元数据产生的IO将会很小。</p>
<h4 id="Write-Amplification"><a href="#Write-Amplification" class="headerlink" title="Write Amplification"></a>Write Amplification</h4><p>注意对于有些实例来说，同步WAL可能会引入不小的写入放大。当写入内容很小时，因为整个block/page可能都需要更新，所以即使在写入很小的情况下，我们可能最终也需要两个4KB的写入（一个用于数据，一个用于元数据）。如果写入的内容只有40byte，将会有8KB被更新，那么写入放大就是8 KB/40 bytes ~= 200。因此，它产生的写入放大可能比LSM树带来的更大。</p>
<h2 id="WAL-Compression"><a href="#WAL-Compression" class="headerlink" title="WAL Compression"></a>WAL Compression</h2><p>WAL 压缩是一项将压缩记录更新到 WAL 的功能。它使用流式压缩来查找记录中的匹配短语，这比基于记录边界压缩的block具有更好的压缩率。</p>
<h3 id="Overview-2"><a href="#Overview-2" class="headerlink" title="Overview"></a>Overview</h3><p>RocksDB中的SST文件包含的是压缩的kv键值对。然而，当用户第一次将键值对写入启用了WAL的数据库，它们写入WAL采用的是非压缩的格式。这样可能是的WAL的文件大小相对于数据库的大小更膨胀。如果数据库位于网络存储上，并且复制了 WAL，则会增加 IO 和存储开销。支持 WAL 压缩解决了这些限制。<br>WAL 以流式传输方式编写和读取。写入数据库的内容可以被打包成逻辑记录并更新到WAL文件中。RocksDB 以 32KB 块的形式分配并物理写入 WAL 文件，超过 32KB 边界的逻辑记录将被分解为物理记录（或片段）。压缩在逻辑记录级别完成，然后分解为物理记录。我们使用流式压缩，它允许在逻辑记录边界处刷新压缩缓冲区，但也允许后续逻辑记录引用先前记录中的匹配短语，与基于块的压缩相比，压缩因子损失最小。<br>这对于具有很长和重复的key的用户特别有用。这在SST文件中不是问题，但是WAL文件与key会不成比例，变得十分巨大。如果 WAL 写入很小并且更频繁地同步到磁盘，但则可能没有那么有益。</p>
<h3 id="Configuration"><a href="#Configuration" class="headerlink" title="Configuration"></a>Configuration</h3><p>WAL的压缩是通过设置<code>DBOptions</code>中的<code>wal_compression</code> 选项来启动的。目前仅支持ZSTD压缩。该配置无法动态改变。无论选项设置如何，RocksDB 都将能够从以前的实例中读取压缩的 WAL 文件（如果存在的话）。</p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a target="_blank" rel="noopener" href="https://wleoo.gitee.io/">诗酒</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a target="_blank" rel="noopener" href="https://wleoo.gitee.io/">https://wleoo.gitee.io/</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">此文章版权归诗酒所有，如有转载，请註明来自原作者</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/WLeoo/tags/RocksDB/">RocksDB</a><a class="post-meta__tags" href="/WLeoo/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/">数据库</a></div><div class="post_share"><div class="social-share" data-image="https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070729354.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/social-share.js/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/social-share.js/dist/js/social-share.min.js" defer></script></div></div><div class="post-reward"><div class="reward-button"><i class="fas fa-qrcode"></i> 打赏</div><div class="reward-main"><ul class="reward-all"><li class="reward-item"><a href="/WLeoo/img/wechat.jpg" target="_blank"><img class="post-qr-code-img" src="/WLeoo/img/wechat.jpg" alt="wechat"/></a><div class="post-qr-code-desc">wechat</div></li><li class="reward-item"><a href="/WLeoo/img/alipay.jpg" target="_blank"><img class="post-qr-code-img" src="/WLeoo/img/alipay.jpg" alt="alipay"/></a><div class="post-qr-code-desc">alipay</div></li></ul></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/WLeoo/2022/12/07/RocksDB%EF%BC%88%E5%9B%9B%EF%BC%89%EF%BC%9AWAL-Practice/"><img class="prev-cover" src="https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070733489.jpg" onerror="onerror=null;src='/WLeoo/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">RocksDB（四）：WAL Practice</div></div></a></div><div class="next-post pull-right"><a href="/WLeoo/2022/12/07/RocksDB%EF%BC%88%E4%BA%8C%EF%BC%89%EF%BC%9AAdministration-and-Data-Access-Tool/"><img class="next-cover" src="https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070727412.jpg" onerror="onerror=null;src='/WLeoo/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">RocksDB（二）：Administration and Data Access Tool</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/WLeoo/2022/12/07/RocksDB%EF%BC%88%E5%9B%9B%EF%BC%89%EF%BC%9AWAL-Practice/" title="RocksDB（四）：WAL Practice"><img class="cover" src="https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070733489.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-12-07</div><div class="title">RocksDB（四）：WAL Practice</div></div></a></div><div><a href="/WLeoo/2022/12/07/RocksDB%EF%BC%88%E4%BA%8C%EF%BC%89%EF%BC%9AAdministration-and-Data-Access-Tool/" title="RocksDB（二）：Administration and Data Access Tool"><img class="cover" src="https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070727412.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-12-07</div><div class="title">RocksDB（二）：Administration and Data Access Tool</div></div></a></div><div><a href="/WLeoo/2022/12/07/RocksDB%EF%BC%88%E4%B8%80%EF%BC%89%EF%BC%9AWhat-is-RocksDB/" title="RocksDB（一）：What is RocksDB"><img class="cover" src="https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070624447.png" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-12-07</div><div class="title">RocksDB（一）：What is RocksDB</div></div></a></div><div><a href="/WLeoo/2022/04/16/%E6%95%B0%E6%8D%AE%E5%BA%93%E5%AE%9E%E9%AA%8C%E6%8A%A5%E5%91%8A%E4%B8%80/" title="《数据库系统原理》实验报告一"><img class="cover" src="https://s2.loli.net/2022/04/29/M3dOgNthEqaFSDy.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2022-04-16</div><div class="title">《数据库系统原理》实验报告一</div></div></a></div></div></div><hr/><div id="post-comment"><div class="comment-head"><div class="comment-headline"><i class="fas fa-comments fa-fw"></i><span> 评论</span></div><div id="comment-switch"><span class="first-comment">Valine</span><span class="switch-btn"></span><span class="second-comment">Disqus</span></div></div><div class="comment-wrap"><div><div class="vcomment" id="vcomment"></div></div><div><div id="disqus_thread"></div></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="/WLeoo/img/avatar.png" onerror="this.onerror=null;this.src='/WLeoo/img/ali.gif'" alt="avatar"/></div><div class="author-info__name">WLeoo</div><div class="author-info__description">但行善举，莫问前程</div></div><div class="card-info-data is-center"><div class="card-info-data-item"><a href="/WLeoo/archives/"><div class="headline">文章</div><div class="length-num">13</div></a></div><div class="card-info-data-item"><a href="/WLeoo/tags/"><div class="headline">标签</div><div class="length-num">17</div></a></div><div class="card-info-data-item"><a href="/WLeoo/categories/"><div class="headline">分类</div><div class="length-num">16</div></a></div></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://github.com/WLeoo"><i class="fab fa-github"></i><span>Follow Me</span></a><div class="card-info-social-icons is-center"><a class="social-icon" href="https://github.com/WLeoo" target="_blank" title="Github"><i class="fab fa-github"></i></a><a class="social-icon" href="/WLeoo/3049458366@qq.com" target="_blank" title="Email"><i class="fas fa-envelope"></i></a></div></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn card-announcement-animation"></i><span>公告</span></div><div class="announcement_content">This is my Blog</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#WAL-Learning"><span class="toc-number">1.</span> <span class="toc-text">WAL Learning</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#Write-Ahead-Log"><span class="toc-number">1.1.</span> <span class="toc-text">Write Ahead Log</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Overview"><span class="toc-number">1.1.1.</span> <span class="toc-text">Overview</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Life-Cycle-of-a-WAL"><span class="toc-number">1.1.2.</span> <span class="toc-text">Life Cycle of a WAL</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#WAL-Configurations"><span class="toc-number">1.1.3.</span> <span class="toc-text">WAL Configurations</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#DBOptions-wal-dir"><span class="toc-number">1.1.3.1.</span> <span class="toc-text">DBOptions::wal_dir</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DBOptions-WAL-ttl-seconds-DBOptions-WAL-size-limit-MB"><span class="toc-number">1.1.3.2.</span> <span class="toc-text">DBOptions::WAL_ttl_seconds,DBOptions::WAL_size_limit_MB</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DBOptions-max-total-wal-size"><span class="toc-number">1.1.3.3.</span> <span class="toc-text">DBOptions::max_total_wal_size</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DBOptions-avoid-flush-during-recovery"><span class="toc-number">1.1.3.4.</span> <span class="toc-text">DBOptions::avoid_flush_during_recovery</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DBOptions-manual-wal-flush"><span class="toc-number">1.1.3.5.</span> <span class="toc-text">DBOptions::manual_wal_flush</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DBOptions-wal-filter"><span class="toc-number">1.1.3.6.</span> <span class="toc-text">DBOptions::wal_filter</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#DBOptions-wal-compression"><span class="toc-number">1.1.3.7.</span> <span class="toc-text">DBOptions::wal_compression</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#WriteOptions-disableWAL"><span class="toc-number">1.1.3.8.</span> <span class="toc-text">WriteOptions::disableWAL</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Transaction-Log-Iterator"><span class="toc-number">1.1.4.</span> <span class="toc-text">Transaction Log Iterator</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#WAL-Recovery-Modes"><span class="toc-number">1.2.</span> <span class="toc-text">WAL Recovery Modes</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Introduction"><span class="toc-number">1.2.1.</span> <span class="toc-text">Introduction</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#kTolerateCorruptedTailRecords"><span class="toc-number">1.3.</span> <span class="toc-text">kTolerateCorruptedTailRecords</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#kAbsoluteConsistency"><span class="toc-number">1.4.</span> <span class="toc-text">kAbsoluteConsistency</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#kPointInTimeRecovery"><span class="toc-number">1.5.</span> <span class="toc-text">kPointInTimeRecovery</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#kSkipAnyCorruptedRecords"><span class="toc-number">1.6.</span> <span class="toc-text">kSkipAnyCorruptedRecords</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#WAL-File-Format"><span class="toc-number">1.7.</span> <span class="toc-text">WAL File Format</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Overview-1"><span class="toc-number">1.7.1.</span> <span class="toc-text">Overview</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#WAL-Manager"><span class="toc-number">1.7.2.</span> <span class="toc-text">WAL Manager</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Reader-Writer"><span class="toc-number">1.8.</span> <span class="toc-text">Reader&#x2F;Writer</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Log-File-Format"><span class="toc-number">1.9.</span> <span class="toc-text">Log File Format</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Record-Format"><span class="toc-number">1.9.1.</span> <span class="toc-text">Record Format</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#The-Legacy-Record-Format"><span class="toc-number">1.9.1.1.</span> <span class="toc-text">The Legacy Record Format</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#The-Recyclable-Record-Format"><span class="toc-number">1.9.1.2.</span> <span class="toc-text">The Recyclable Record Format</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Record-Format-Details-For-Legacy-Format"><span class="toc-number">1.9.1.3.</span> <span class="toc-text">Record Format Details For Legacy Format</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#payload"><span class="toc-number">1.9.1.4.</span> <span class="toc-text">payload</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Benefits"><span class="toc-number">1.9.2.</span> <span class="toc-text">Benefits</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Downsides"><span class="toc-number">1.9.3.</span> <span class="toc-text">Downsides</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#WAL-Performance"><span class="toc-number">1.10.</span> <span class="toc-text">WAL Performance</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Non-Sync-Mode"><span class="toc-number">1.10.1.</span> <span class="toc-text">Non-Sync Mode</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Sync-Mode"><span class="toc-number">1.10.2.</span> <span class="toc-text">Sync Mode</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Group-Commit"><span class="toc-number">1.10.2.1.</span> <span class="toc-text">Group Commit</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Number-of-I-Os-per-write"><span class="toc-number">1.10.2.2.</span> <span class="toc-text">Number of I&#x2F;Os per write</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Write-Amplification"><span class="toc-number">1.10.2.3.</span> <span class="toc-text">Write Amplification</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#WAL-Compression"><span class="toc-number">1.11.</span> <span class="toc-text">WAL Compression</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#Overview-2"><span class="toc-number">1.11.1.</span> <span class="toc-text">Overview</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Configuration"><span class="toc-number">1.11.2.</span> <span class="toc-text">Configuration</span></a></li></ol></li></ol></li></ol></div></div><div class="card-widget card-recent-post"><div class="item-headline"><i class="fas fa-history"></i><span>最新文章</span></div><div class="aside-list"><div class="aside-list-item"><a class="thumbnail" href="/WLeoo/2022/12/07/OpenEC%EF%BC%9A%E9%9D%A2%E5%90%91%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E7%9A%84%E7%BB%9F%E4%B8%80%E5%8F%AF%E9%85%8D%E7%BD%AE%E6%93%A6%E9%99%A4%E7%BC%96%E7%A0%81%E7%AE%A1%E7%90%86/" title="OpenEC：面向分布式系统的统一可配置擦除编码管理"><img src="https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070742533.jpg" onerror="this.onerror=null;this.src='/WLeoo/img/404.jpg'" alt="OpenEC：面向分布式系统的统一可配置擦除编码管理"/></a><div class="content"><a class="title" href="/WLeoo/2022/12/07/OpenEC%EF%BC%9A%E9%9D%A2%E5%90%91%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E7%9A%84%E7%BB%9F%E4%B8%80%E5%8F%AF%E9%85%8D%E7%BD%AE%E6%93%A6%E9%99%A4%E7%BC%96%E7%A0%81%E7%AE%A1%E7%90%86/" title="OpenEC：面向分布式系统的统一可配置擦除编码管理">OpenEC：面向分布式系统的统一可配置擦除编码管理</a><time datetime="2022-12-06T23:41:12.000Z" title="发表于 2022-12-07 07:41:12">2022-12-07</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/WLeoo/2022/12/07/RocksDB%EF%BC%88%E5%9B%9B%EF%BC%89%EF%BC%9AWAL-Practice/" title="RocksDB（四）：WAL Practice"><img src="https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070733489.jpg" onerror="this.onerror=null;this.src='/WLeoo/img/404.jpg'" alt="RocksDB（四）：WAL Practice"/></a><div class="content"><a class="title" href="/WLeoo/2022/12/07/RocksDB%EF%BC%88%E5%9B%9B%EF%BC%89%EF%BC%9AWAL-Practice/" title="RocksDB（四）：WAL Practice">RocksDB（四）：WAL Practice</a><time datetime="2022-12-06T20:58:44.000Z" title="发表于 2022-12-07 04:58:44">2022-12-07</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/WLeoo/2022/12/07/RocksDB%EF%BC%88%E4%B8%89%EF%BC%89%EF%BC%9AWAL-Learning/" title="RocksDB（三）：WAL Learning"><img src="https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070729354.jpg" onerror="this.onerror=null;this.src='/WLeoo/img/404.jpg'" alt="RocksDB（三）：WAL Learning"/></a><div class="content"><a class="title" href="/WLeoo/2022/12/07/RocksDB%EF%BC%88%E4%B8%89%EF%BC%89%EF%BC%9AWAL-Learning/" title="RocksDB（三）：WAL Learning">RocksDB（三）：WAL Learning</a><time datetime="2022-12-06T20:38:42.000Z" title="发表于 2022-12-07 04:38:42">2022-12-07</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/WLeoo/2022/12/07/RocksDB%EF%BC%88%E4%BA%8C%EF%BC%89%EF%BC%9AAdministration-and-Data-Access-Tool/" title="RocksDB（二）：Administration and Data Access Tool"><img src="https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070727412.jpg" onerror="this.onerror=null;this.src='/WLeoo/img/404.jpg'" alt="RocksDB（二）：Administration and Data Access Tool"/></a><div class="content"><a class="title" href="/WLeoo/2022/12/07/RocksDB%EF%BC%88%E4%BA%8C%EF%BC%89%EF%BC%9AAdministration-and-Data-Access-Tool/" title="RocksDB（二）：Administration and Data Access Tool">RocksDB（二）：Administration and Data Access Tool</a><time datetime="2022-12-06T20:32:00.000Z" title="发表于 2022-12-07 04:32:00">2022-12-07</time></div></div><div class="aside-list-item"><a class="thumbnail" href="/WLeoo/2022/12/07/RocksDB%EF%BC%88%E4%B8%80%EF%BC%89%EF%BC%9AWhat-is-RocksDB/" title="RocksDB（一）：What is RocksDB"><img src="https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070624447.png" onerror="this.onerror=null;this.src='/WLeoo/img/404.jpg'" alt="RocksDB（一）：What is RocksDB"/></a><div class="content"><a class="title" href="/WLeoo/2022/12/07/RocksDB%EF%BC%88%E4%B8%80%EF%BC%89%EF%BC%9AWhat-is-RocksDB/" title="RocksDB（一）：What is RocksDB">RocksDB（一）：What is RocksDB</a><time datetime="2022-12-06T19:23:41.000Z" title="发表于 2022-12-07 03:23:41">2022-12-07</time></div></div></div></div></div></div></main><footer id="footer" style="background-image: url('https://blog-imgs-1315248452.cos.ap-nanjing.myqcloud.com/images/202212070729354.jpg')"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2022 By WLeoo</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div><div class="footer_custom_text">Hi, welcome to my blog!</div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="translateLink" type="button" title="简繁转换">简</button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="chat_btn" type="button" title="rightside.chat_btn"><i class="fas fa-sms"></i></button><a id="to_comment" href="#post-comment" title="直达评论"><i class="fas fa-comments"></i></a><button id="go-up" type="button" title="回到顶部"><i class="fas fa-arrow-up"></i></button></div></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">本地搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div></div><div><script src="/WLeoo/js/utils.js"></script><script src="/WLeoo/js/main.js"></script><script src="/WLeoo/js/tw_cn.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.js"></script><script src="/WLeoo/js/search/local-search.js"></script><script>var preloader = {
  endLoading: () => {
    document.body.style.overflow = 'auto';
    document.getElementById('loading-box').classList.add("loaded")
  },
  initLoading: () => {
    document.body.style.overflow = '';
    document.getElementById('loading-box').classList.remove("loaded")

  }
}
window.addEventListener('load',preloader.endLoading())</script><div class="js-pjax"><script>function loadValine () {
  function initValine () {
    const valine = new Valine(Object.assign({
      el: '#vcomment',
      appId: 'FxBfa6mfRNowzptPvsvHni4L-gzGzoHsz',
      appKey: '5PkFiNt3LOiRXfEDGFsGov5p',
      avatar: 'monsterid',
      serverURLs: '',
      emojiMaps: "",
      path: window.location.pathname,
      visitor: true
    }, null))
  }

  if (typeof Valine === 'function') initValine() 
  else getScript('https://cdn.jsdelivr.net/npm/valine/dist/Valine.min.js').then(initValine)
}

if ('Valine' === 'Valine' || !false) {
  if (false) btf.loadComment(document.getElementById('vcomment'),loadValine)
  else setTimeout(loadValine, 0)
} else {
  function loadOtherComment () {
    loadValine()
  }
}</script><script>function loadDisqus () {
  var disqus_config = function () {
    this.page.url = 'https://gitee.com/WLeoo/wleoo.git/2022/12/07/RocksDB%EF%BC%88%E4%B8%89%EF%BC%89%EF%BC%9AWAL-Learning/'
    this.page.identifier = '2022/12/07/RocksDB（三）：WAL-Learning/'
    this.page.title = 'RocksDB（三）：WAL Learning'
  };

  window.disqusReset = () => {
    DISQUS.reset({
      reload: true,
      config: disqus_config
    })
  }

  if (window.DISQUS) disqusReset()
  else {
    (function() { 
      var d = document, s = d.createElement('script');
      s.src = 'https://.disqus.com/embed.js';
      s.setAttribute('data-timestamp', +new Date());
      (d.head || d.body).appendChild(s);
    })();
  }
}

if ('Valine' === 'Disqus' || !false) {
  if (false) btf.loadComment(document.getElementById('disqus_thread'), loadDisqus)
  else loadDisqus()
} else {
  function loadOtherComment () {
    loadDisqus()
  }
}
</script></div><script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script><script src="https://cdn.jsdelivr.net/gh/xiabo2/CDN@latest/fishes.js"></script><div class="aplayer no-destroy" data-id="60198" data-server="netease" data-type="playlist" data-fixed="true" data-autoplay="false"> </div><script src="/WLeoo/js/wleoo.js"></script><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js"></script><script src="https://cdn.jsdelivr.net/gh/metowolf/MetingJS@1.2/dist/Meting.min.js"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div><!-- hexo injector body_end start --><script data-pjax>function history_calendar_injector_config(){
                var parent_div_git = document.getElementsByClassName('sticky_layout')[0];
                var item_html = '<div class="card-widget card-history"><div class="card-content"><div class="item-headline"><i class="fas fa-clock fa-spin"></i><span>那年今日</span></div><div id="history-baidu" style="height: 100px;overflow: hidden"><div class="history_swiper-container" id="history-container" style="width: 100%;height: 100%"><div class="swiper-wrapper" id="history_container_wrapper" style="height:20px"></div></div></div></div>';
                console.log('已挂载history_calendar')
                // parent_div_git.innerHTML=item_html+parent_div_git.innerHTML // 无报错，但不影响使用(支持pjax跳转)
                parent_div_git.insertAdjacentHTML("afterbegin",item_html) // 有报错，但不影响使用(支持pjax跳转)
            }if( document.getElementsByClassName('sticky_layout')[0] && (location.pathname ==='all'|| 'all' ==='all')){

            history_calendar_injector_config()
        } </script><script data-pjax  src="https://cdn.jsdelivr.net/npm/swiper/swiper-bundle.min.js"></script><script data-pjax src="https://cdn.jsdelivr.net/gh/Zfour/Butterfly-card-history/baiduhistory/js/main.js"></script><!-- hexo injector body_end end --></body></html>